package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.model.user.VipServiceConfig;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {


    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private OrderDerateMapper orderDerateMapper;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private TrackInfoFeignClient trackInfoFeignClient;

    @Autowired
    private RedissonClient redissonClient;



    @Override
    public OrderInfoVo trade(TradeVo tradeVo, Long userId) {
        Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(userId);
        Assert.notNull(userInfoVoResult, "Shut Fuck Up , Are you kidding me ?");
        UserInfoVo userInfoVo = userInfoVoResult.getData();
        Assert.notNull(userInfoVo, "Shut Fuck Up , Are you kidding me ?");

        //  订单原始金额
        BigDecimal originalAmount = new BigDecimal("0.00");
        //  减免总金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        //  订单总价
        BigDecimal orderAmount = new BigDecimal("0.00");
        //  订单明细集合
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //  订单减免明细列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        String itemType = tradeVo.getItemType();

        if (tradeVo.getItemType().equals(SystemConstant.ORDER_ITEM_TYPE_ALBUM)) {
            Result<Boolean> isPaidAlbumList = this.userInfoFeignClient.isPaidAlbum(tradeVo.getItemId());
            Assert.notNull(isPaidAlbumList, "What're you doing ? Man");
            Boolean isPaidAlbum = isPaidAlbumList.getData();
            Assert.notNull(isPaidAlbum, "Hey! Man! ! !");
            if (isPaidAlbum) {
                throw new GuiguException(ResultCodeEnum.REPEAT_BUY_ERROR);
            }
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(tradeVo.getItemId());
            Assert.notNull(albumInfoResult, "What Can i say?");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "Mamba Out!");

            if (userInfoVo.getIsVip().intValue() == 0 || userInfoVo.getIsVip().intValue() == 1 && userInfoVo.getVipExpireTime().before(new Date())) {
                originalAmount = albumInfo.getPrice();

                orderAmount = originalAmount.subtract(derateAmount);
            } else {
                //原始价格
                originalAmount = albumInfo.getPrice();
                //优惠价格 derateAmount  -1表示没有折扣
                if (albumInfo.getVipDiscount().intValue() != -1) {
                    //100打8折，优惠20
                    // 原价*（10-折扣） / 10
                    derateAmount = this.GG(albumInfo.getVipDiscount(), originalAmount);
                }

                // 最终价格
                orderAmount = originalAmount.subtract(derateAmount);
            }
            //  订单明细集合 List<OrderDetailVo> orderDetailVoList
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName(albumInfo.getAlbumTitle());
            orderDetailVo.setItemUrl(albumInfo.getCoverUrl());
            orderDetailVo.setItemPrice(albumInfo.getPrice());
            orderDetailVoList.add(orderDetailVo);

            //订单减免明细列表  List<OrderDerateVo>  orderDerateVoList
            // 原始价格 - 最终价格 != 0 有优惠
            if (originalAmount.subtract(orderAmount).doubleValue() != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT);
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVoList.add(orderDerateVo);
            }

        } else if ("1003".equals(itemType)) { //4 购买vip
            //1 远程调用：根据vip的id获取vip信息
            Result<VipServiceConfig> result = vipServiceConfigFeignClient.getVipServiceConfig(tradeVo.getItemId());
            VipServiceConfig vipServiceConfig = result.getData();
            Assert.notNull(vipServiceConfig, "vip数据为空");

            //2 计算三个价格：原始价格  、 优惠价格  、最终价格
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            derateAmount = originalAmount.subtract(orderAmount);

            // 3 订单明细集合 List<OrderDetailVo> orderDetailVoList
            //4 订单减免明细列表  List<OrderDerateVo>  orderDerateVoList
            OrderDetailVo orderDetailVo = new OrderDetailVo();
            orderDetailVo.setItemId(tradeVo.getItemId());
            orderDetailVo.setItemName("VIP会员" + vipServiceConfig.getName());
            orderDetailVo.setItemUrl(vipServiceConfig.getImageUrl());
            orderDetailVo.setItemPrice(vipServiceConfig.getDiscountPrice());
            orderDetailVoList.add(orderDetailVo);

            //添加订单减免
            if (originalAmount.subtract(orderAmount).doubleValue() != 0) {
                OrderDerateVo orderDerateVo = new OrderDerateVo();
                orderDerateVo.setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT);
                orderDerateVo.setDerateAmount(originalAmount.subtract(orderAmount));
                orderDerateVoList.add(orderDerateVo);
            }

        } else if ("1002".equals(itemType)) { //TODO 5 购买声音
            if (tradeVo.getTrackCount().intValue() < 0) {
                throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
            }

            //远程调用：查询当前购买声音数据列表
            //传递声音id 和 购买数量
            Result<List<TrackInfo>> trackInfoListResult = trackInfoFeignClient.findPaidTrackInfoList(tradeVo.getItemId(), tradeVo.getTrackCount());
            List<TrackInfo> trackInfoList = trackInfoListResult.getData();

            TrackInfo trackInfo = trackInfoList.get(0);
            Long albumId = trackInfo.getAlbumId();

            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            AlbumInfo albumInfo = albumInfoResult.getData();
            BigDecimal price = albumInfo.getPrice();
            originalAmount = tradeVo.getTrackCount().intValue() > 0 ? albumInfo.getPrice().multiply(new BigDecimal(tradeVo.getTrackCount())) : albumInfo.getPrice();
            orderAmount = originalAmount;

            //订单明细
            //List<OrderDetailVo> orderDetailVoList
            // List<TrackInfo> -- List<OrderDetailVo>
            orderDetailVoList = trackInfoList.stream().map(trackInfo1 -> {
                OrderDetailVo orderDetailVo = new OrderDetailVo();
                orderDetailVo.setItemId(trackInfo1.getId());
                orderDetailVo.setItemUrl(trackInfo1.getCoverUrl());
                orderDetailVo.setItemPrice(albumInfo.getPrice());
                orderDetailVo.setItemName(trackInfo1.getTrackTitle());
                return orderDetailVo;
            }).collect(Collectors.toList());
        }

        //6 把这些数据最终封装到 OrderInfoVo
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        //购买类型 ：vip 专辑  声音
        orderInfoVo.setItemType(tradeVo.getItemType());
        //原始价格
        orderInfoVo.setOriginalAmount(originalAmount);
        //优惠价格
        orderInfoVo.setDerateAmount(derateAmount);
        //最终价格
        orderInfoVo.setOrderAmount(orderAmount);
        //订单明细
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        //优惠明细
        orderInfoVo.setOrderDerateVoList(orderDerateVoList);

        //7 防止重复提交
        //防止重复提交，添加数据到redis里面
        String tradeNoKey = "user:trade:" + userId;
        // 定义一个流水号
        String tradeNo = UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(tradeNoKey, tradeNo);
        //向后传递tradeNo
        orderInfoVo.setTradeNo(tradeNo);

        //8 数据安全校验
        //  生成签名
        Map<String, Object> parameterMap =
                JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        String sign = SignHelper.getSign(parameterMap);
        orderInfoVo.setSign(sign);

        //9 返回OrderInfoVo对象数据
        return orderInfoVo;
    }

    @Override
    public String submitOrder(OrderInfoVo orderInfoVo, Long userId) {
        //1 校验数据安全问题
        //  校验签名

        Map map = JSON.parseObject(JSON.toJSONString(orderInfoVo), Map.class);
        map.put("payWay",SystemConstant.ORDER_PAY_WAY_WEIXIN);
        SignHelper.checkSign(map);


        String tradeNo = orderInfoVo.getTradeNo();
        if (StringUtils.isEmpty(tradeNo)){
            //  非法提交
            throw  new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        String tradeNoKey = "user:trade:" + userId;
        String script = "if(redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]) else return 0 end";

        RedisScript<Boolean> redisScript = new DefaultRedisScript<>(script, Boolean.class);

        Boolean flag = (Boolean) redisTemplate.execute(redisScript,
                Arrays.asList(tradeNoKey),
                tradeNo);
        if(!flag) {//重复提交
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }


        //判断支付类型：1101-微信 1103-账户余额
        String orderNo = UUID.randomUUID().toString().replace("-", "");
        String payWay = orderInfoVo.getPayWay();
        if("1101".equals(payWay)) { //微信支付
            //调用方法添加订单数据
            this.saveOrder(orderInfoVo,userId,orderNo);
        } else if("1103".equals(payWay)) {//余额支付
            //远程调用实现：检查余额是否充足，如果余额充足，扣减余额
            //封装需要数据到AccountLockVo
            AccountLockVo accountDeductVo = new AccountLockVo();
            accountDeductVo.setOrderNo(orderNo);//订单编号
            accountDeductVo.setUserId(userId);//用户id
            accountDeductVo.setAmount(orderInfoVo.getOrderAmount());//支付金额
            //购买商品名称
            accountDeductVo.setContent(orderInfoVo.getOrderDetailVoList().get(0).getItemName());
            //远程调用
            Result result = userAccountFeignClient.checkAndDeduct(accountDeductVo);
            if(result.getCode() != 200) {
                throw new GuiguException(result.getCode(),result.getMessage());
            }

            //调用方法添加订单数据
            OrderInfo orderInfo = this.saveOrder(orderInfoVo,userId,tradeNo);

            //远程调用：添加购买记录
            //使用UserPaidRecordVo封装添加购买需要数据
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            //订单编号
            userPaidRecordVo.setOrderNo(orderNo);
            //用户id
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            //购买类型：vip、专辑、声音
            userPaidRecordVo.setItemType(orderInfo.getItemType());

            //  购买项目的Id 专辑Id，声音Id, 服务配置Id
            List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream()
                    .map(OrderDetailVo::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);

            Result userResult = userInfoFeignClient.savePaidRecord(userPaidRecordVo);
            //  判断
            if (200 != userResult.getCode()) {
                throw new GuiguException(211, "新增购买记录异常");
            }
        }
        //订单编号
        return orderNo;
    }

    //取消订单
    @Override
    public void orderCancel(Long orderId) {
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if(orderInfo.getOrderStatus().equals("0901")) {//未支付进行取消操作
            orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_CANCEL);
            orderInfoMapper.updateById(orderInfo);
        }
    }

    @Override
    public void orderPaySuccess(String orderNo) {
        //1 根据orderNo查询订单数据
        OrderInfo orderInfo = this.getOrderInfoByOrderNo(orderNo);

        //2 设置订单状态：已经支付  订单状态：0901-未支付 0902-已支付 0903-已取消
        if(orderInfo.getOrderStatus().equals("0901")) {
            orderInfo.setOrderStatus("0902");
            //3 调用mapper方法进行修改
            orderInfoMapper.updateById(orderInfo);
        }

        //  记录用户购买信息: 微信支付就需要再次调用；余额支付不需要！
        if (orderInfo.getPayWay().equals(SystemConstant.ORDER_PAY_WAY_WEIXIN)){
            //  添加一个判断：
            //  保存用户购买记录：
            UserPaidRecordVo userPaidRecordVo = new UserPaidRecordVo();
            userPaidRecordVo.setOrderNo(orderNo);
            userPaidRecordVo.setUserId(orderInfo.getUserId());
            userPaidRecordVo.setItemType(orderInfo.getItemType());
            //  [专辑Id;vip服务配置Id;下单的时候只能购买一个，这个明细数据就是一条] 声音Id{应该是集合}
            //  以上Id都在? orderInfoVo.getOrderDetailVoList(); 订单明细表中有itemId;
            //  List<Long> itemIdList = orderInfoVo.getOrderDetailVoList().stream().map(OrderDetailVo::getItemId).collect(Collectors.toList());
            //  声音只支持余额支付！
            List<Long> itemIdList = orderInfo.getOrderDetailList().stream().map(OrderDetail::getItemId).collect(Collectors.toList());
            userPaidRecordVo.setItemIdList(itemIdList);
            //  远程调用保存用户交易记录。
            this.userInfoFeignClient.savePaidRecord(userPaidRecordVo);
        }
    }

    //保存订单数据
//微信支付 和 余额支付都需要保存订单数据
    public OrderInfo saveOrder(OrderInfoVo orderInfoVo, Long userId, String orderNo) {
        //有三张表
        //1 添加订单基本信息到 order_info表
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo,orderInfo);
        orderInfo.setOrderNo(orderNo);//订单编号
        orderInfo.setUserId(userId);//用户id
        String orderTitle = orderInfoVo.getOrderDetailVoList().get(0).getItemName();
        orderInfo.setOrderTitle(orderTitle);
        //订单状态
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);
        orderInfoMapper.insert(orderInfo);

        //2 添加订单明细 order_detail
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (!CollectionUtils.isEmpty(orderDetailVoList)) {

            orderDetailVoList.forEach(orderDetailVo -> {
                OrderDetail orderDetail = new OrderDetail();
                BeanUtils.copyProperties(orderDetailVo,orderDetail);
                orderDetail.setOrderId(orderInfo.getId());
                orderDetailMapper.insert(orderDetail);

            });
        }

        //3 优惠明细 order_derate
        //购买声音时候，没有优惠
        // 订单减免
        if (!CollectionUtils.isEmpty(orderInfoVo.getOrderDerateVoList())) {
            orderInfoVo.getOrderDerateVoList().forEach(orderDerateVo -> {
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderInfo.getId());
                orderDerateMapper.insert(orderDerate);
            });
        }

        //4 判断：余额支付，修改订单状态已经支付
        // 订单支付方式 1101-微信 1102-支付宝 1103-账户余额
        if (SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfo.getPayWay())) {
            // 余额支付成功保存交易数据,订单状态已经支付
            this.orderPaySuccess(orderNo);

        } else { //5 判断：微信支付，发送延迟消息
            // Redisson发送延迟队列，如果定时未支付，取消订单
            // 使用rabbitmq实现
            //1 ttl + 死信队列
            //2 延迟插件
            // Redisson实现延迟消息
            this.sendDelayMessage(orderInfo.getId());
        }

        return orderInfo;
    }

    //发送消息到延迟队列
    public void sendDelayMessage(Long id) {
        //1 创建普通队列
        RBlockingQueue<Object> blockingQueue =
                redissonClient.getBlockingQueue(MqConst.EXCHANGE_CANCEL_ORDER);

        //2 把普通队列变成延迟队列
        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingQueue);

        //3 向延迟队列发送消息，设置延迟时间
        //delayedQueue.offer(id.toString(),30,TimeUnit.MINUTES);
        delayedQueue.offer(id.toString(),10, TimeUnit.SECONDS);
    }

    @Override
    public OrderInfo getOrderInfoByOrderNo(String orderNo) {
        LambdaQueryWrapper<OrderInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(OrderInfo::getOrderNo,orderNo);
        OrderInfo orderInfo = orderInfoMapper.selectOne(wrapper);

        List<OrderDetail> orderDetailList =
                orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>()
                        .eq(OrderDetail::getOrderId, orderInfo.getId()));

        orderInfo.setOrderDetailList(orderDetailList);
        return orderInfo;
    }

    private BigDecimal GG(BigDecimal T, BigDecimal originalAmount) {
        //  减免总金额
        BigDecimal derateAmount = new BigDecimal("0.00");
        derateAmount = originalAmount.multiply(new BigDecimal("10").subtract(T)).divide(new BigDecimal("10"), 2, RoundingMode.HALF_UP);
        return derateAmount;
    }
}
